// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen.go

// Package ccitt implements a CCITT (fax) image decoder.
package ccitt

import (
	"encoding/binary"
	"errors"
	"io"
	"math/bits"
)

var (
	errInvalidCode = errors.New("ccitt: invalid code")
)

// Order specifies the bit ordering in a CCITT data stream.
type Order uint32

const (
	// LSB means Least Significant Bits first.
	LSB Order = iota
	// MSB means Most Significant Bits first.
	MSB
)

type bitReader struct {
	r io.Reader

	// readErr is the error returned from the most recent r.Read call. As the
	// io.Reader documentation says, when r.Read returns (n, err), "always
	// process the n > 0 bytes returned before considering the error err".
	readErr error

	order Order

	// The low nBits bits of the bits field hold upcoming bits in LSB order.
	bits  uint64
	nBits uint32

	// bytes[br:bw] holds bytes read from r but not yet loaded into bits.
	br    uint32
	bw    uint32
	bytes [1024]uint8
}

func (b *bitReader) alignToByteBoundary() {
	n := b.nBits & 7
	b.bits >>= n
	b.nBits -= n
}

func (b *bitReader) nextBit() (uint32, error) {
	for {
		if b.nBits > 0 {
			bit := uint32(b.bits) & 1
			b.bits >>= 1
			b.nBits--
			return bit, nil
		}

		if available := b.bw - b.br; available >= 8 {
			b.bits = binary.LittleEndian.Uint64(b.bytes[b.br:])
			b.br += 8
			b.nBits = 64
			continue
		} else if available > 0 {
			b.bits = uint64(b.bytes[b.br])
			b.br++
			b.nBits = 8
			continue
		}

		if b.readErr != nil {
			return 0, b.readErr
		}

		n, err := b.r.Read(b.bytes[:])
		b.br = 0
		b.bw = uint32(n)
		b.readErr = err

		if b.order != LSB {
			written := b.bytes[:b.bw]
			for i, x := range written {
				written[i] = bits.Reverse8(x)
			}
		}
	}
}

func decode(b *bitReader, table [][2]int16) (uint32, error) {
	for state := int32(1); ; {
		bit, err := b.nextBit()
		if err != nil {
			return 0, err
		}
		// The "&1" is redundant, but can eliminate a bounds check.
		state = int32(table[state][bit&1])
		if state < 0 {
			return uint32(^state), nil
		} else if state == 0 {
			return 0, errInvalidCode
		}
	}
}
